package com.hackordie.commons.software.OperativeSistem;

import java.util.ArrayList;
import java.util.List;

import com.hackordie.commons.game.player.PlayersControl;
import com.hackordie.commons.game.world.RouterMap;
import com.hackordie.commons.hardware.components.AComponent;
import com.hackordie.commons.hardware.components.Firewall;
import com.hackordie.commons.hardware.components.NetworkCard;
import com.hackordie.commons.hardware.computer.Computer;
import com.hackordie.commons.hardware.computer.IComputer;
import com.hackordie.commons.hardware.router.Router;
import com.hackordie.commons.software.AProgram;
import com.hackordie.constants.EComponentsType;
import com.hackordie.constants.ESoftwareType;

public abstract class AOperativeSistem extends AProgram {

    private String userName;
    private String password;
    
    private List<IComputer> connectedTo = new ArrayList<IComputer>();
    
    public AOperativeSistem(String name, String description, int size,
            int requiredRam, int requiredProcessor) {
        super(name, description, size, ESoftwareType.operativeSystem, requiredRam, requiredProcessor);
    }

    
    //setters and getters
    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    
    public void inititateConnectedTo(IComputer pc) {
        connectedTo.add(pc);
        
    }
    
    
    // functions

    public boolean connectToRouter(String ip, RouterMap routersMap, int[] firewallKeys) {

        // solo se puede conectar a los ordenadores que estan conectados entre
        // si
        // osea, a los ordenadores cuya IP sea alguna de las ips que se
        // encuentran
        // en NetworkCard.connectedToIp

        // el primer pc al que estara siempre conectado es al suyo propio    
                    
        // buscamos todos los componentes, si son "NetworkCard" miramos su
        // "connectedToIp", si
        // coincide con la ip a la que intentamos conectar, la agregamos a la
        // lista

        int pos = 0;
        boolean found = false;

        // comenzamos por el ultimo ordenador al que esta actualmente conectado
        List<AComponent> components = connectedTo.get(connectedTo.size() - 1)
                .getComponentsList();

        while (pos < components.size() && !found) {
            // buscamos todas las NetworkCard que pueda tener
            if (components.get(pos).getComponentType() == EComponentsType.NetworkCard) {
                // comprobamos que esa networkCard este conectada a la ip
                // deseada y este encendida
                if (((NetworkCard) components.get(pos)).isStarted()
                        && ((NetworkCard) components.get(pos))
                                .getConnectedToIp().equals(ip)) {

                    // comprobamos que al menos una networkCard del pc objetivo
                    // este encendida

                    List<AComponent> objetiveNetworkCards = routersMap
                            .getRouterByIp(ip).getComponentByType(
                                    EComponentsType.NetworkCard);

                    //comprobamos que la lista de firewalls coincida con la cantidad de 
                    //firewallsKeys                    
                    List<AComponent> objetiveFirewalls = routersMap
                            .getRouterByIp(ip).getComponentByType(
                                    EComponentsType.Firewall);
                    
                    if(objetiveFirewalls.size() != firewallKeys.length) {
                        return false;
                    }
                    
                    
                    for (int i = 0; i < objetiveNetworkCards.size(); i++) {
                        if (objetiveNetworkCards.get(i).isStarted()) {

                            //comprobamos que tenemos todas las claves de firewalls correctas
                            for(int j = 0; j < objetiveFirewalls.size(); j++) {
                                if(! (((Firewall)objetiveFirewalls.get(j)).getAccesKey() == firewallKeys[j])) {
                                    return false;  //si fallamos una clave de firewall, no conectamos
                                }
                            }                            
                            
                            // guardamos la ip del ordenador a donde estamos
                            // actualmente conectados
                            String actuallyConnectedIp = ((Computer) connectedTo
                                    .get(connectedTo.size() - 1)).getMyIp();

                            // agregamos un nuevo ordenador a las conexiones
                            // actuales
                            connectedTo.add(routersMap.getRouterByIp(ip));

                            // agregamos el actual componente a la lista de
                            // "componentes conectados"
                            // del componente objetivo
                            routersMap.getRouterByIp(ip).addNewIpConnected(
                                    actuallyConnectedIp);
                            return true;
                        }
                    }
                }
            }
            pos++;
        }
        return false;
    }

    public boolean connectToPc(String ip, PlayersControl playersOnline, int[] firewallKeys) {

        // conectar a un pc que este conectado al router al que estamos
        // conectados
        Router router = (Router) connectedTo.get(connectedTo.size() - 1);

        boolean found = false;
        int pos = 0;

        while (!found && pos < router.getIpActuallyConnected().size()) {
            if (router.getIpActuallyConnected().get(pos).equals(ip)) {
                
                IComputer enemyPc = playersOnline.getPlayerByIp(ip).getPc();                
                
                //comprobamos que la lista de firewalls coincida con la cantidad de 
                //firewallsKeys                    
                List<AComponent> objetiveFirewalls = enemyPc.getComponentByType(EComponentsType.Firewall);                
                if(objetiveFirewalls.size() != firewallKeys.length) {
                    return false;
                }
                
                //comprobamos que tenemos todas las claves de firewalls correctas
                for(int j = 0; j < objetiveFirewalls.size(); j++) {
                    if(! (((Firewall)objetiveFirewalls.get(j)).getAccesKey() == firewallKeys[j])) {
                        return false;  //si fallamos una clave de firewall, no conectamos
                    }
                }                            
                
                connectedTo.add(enemyPc);
                return true;
            }
            pos++;
        }
        return false;
    }

    public IComputer getConnectedTo() {
        return connectedTo.get(connectedTo.size() - 1);
    }

    public List<IComputer> getAllconnections() {
        return connectedTo;
    }

    public List<String> getAllComputersConnectedToThis() {

        if (connectedTo.get(connectedTo.size() - 1) instanceof Router) {
            ((Router) connectedTo.get(connectedTo.size() - 1))
                    .getIpActuallyConnected();
        }
        return null;
    }
  
    public void disconnectFromActual() {
        if (connectedTo.size() > 1) {
            // buscamos en su lista de "ipActuallyConnected" por la ip
            // correspondiente al
            // ordenador desde donde estamos conectados, y la quitamos, si es un
            // router
            if (connectedTo.get(connectedTo.size() - 1) instanceof Router) {
                ((Router) connectedTo.get(connectedTo.size() - 1))
                        .removeNewIpConnected(connectedTo.get(
                                connectedTo.size() - 2).getMyIp());
            }

            // removemos el ordenador de la lista de conexiones
            connectedTo.remove(connectedTo.size() - 1);
        }
    }

    public void disconnectFromAll() {
        for (int i = connectedTo.size() - 1; i > 0; i--) {
            disconnectFromActual();
        }
    }
    

}
